#include "network/include/VsomeipServer.h"
#include "network/include/Def.h"
#include "trace/include/logger.h"

#include <vsomeip/handler.hpp>
#include <vsomeip/internal/logger.hpp>
#include <vsomeip/enumeration_types.hpp>

#include <iomanip>
#include <iostream>
#include <sstream>

namespace network
{
    
VsomeipServer::VsomeipServer(bool _use_tcp)
 : itsApp(vsomeip::runtime::get()->create_application("service-sample"))
 , use_tcp_(_use_tcp)
{
}

bool VsomeipServer::init() 
{
    if (!itsApp->init()) {
        std::cerr << "Couldn't initialize application" << std::endl;
        return false;
    }
    itsApp->register_state_handler(
            std::bind(&VsomeipServer::onState, this,
                    std::placeholders::_1));

    itsApp->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_GET_METHOD_ID, std::bind(&VsomeipServer::onGet, this, std::placeholders::_1));
    itsApp->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_SET_METHOD_ID, std::bind(&VsomeipServer::onSet, this, std::placeholders::_1));
    itsApp->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_METHOD_ID, std::bind(&VsomeipServer::onMessage, this, std::placeholders::_1));
    itsApp->register_message_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, vsomeip::ANY_METHOD, std::bind(&VsomeipServer::onUnknownMethod, this, std::placeholders::_1));
    itsApp->register_message_handler(OTHER_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, OTHER_SAMPLE_METHOD_ID, std::bind(&VsomeipServer::onMessage, this, std::placeholders::_1));
    itsApp->register_message_handler(SAMPLE_SERVICE_ID, OTHER_SAMPLE_INSTANCE_ID, SAMPLE_METHOD_ID, std::bind(&VsomeipServer::onMessage, this, std::placeholders::_1));
    itsApp->register_subscription_handler(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENTGROUP_ID, std::bind(&VsomeipServer::onRegisterSubscription, this, 
                                          std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));

    std::set<vsomeip::eventgroup_t> its_groups;
    its_groups.insert(SAMPLE_EVENTGROUP_ID);
    itsApp->offer_event(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, its_groups, vsomeip::event_type_e::ET_FIELD);

    std::set<vsomeip::eventgroup_t> its_groups_e2e;
    its_groups_e2e.insert(E2E_EVENTGROUP_ID);
    itsApp->offer_event(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID, E2E_EVENT_ID, its_groups_e2e, vsomeip::event_type_e::ET_FIELD);
    itsApp->offer_event(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID, E2E_EVENT_ID_2, its_groups_e2e, vsomeip::event_type_e::ET_FIELD);

    return true;
}

void VsomeipServer::start() 
{
    offer();
    itsApp->start();
}

 void VsomeipServer::stop() 
{
    itsApp->clear_all_handler();
    stop_offer();
    itsApp->stop();
}

void VsomeipServer::offer() 
{
    itsApp->offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    // itsApp->offer_service(SAMPLE_SERVICE_ID, OTHER_SAMPLE_INSTANCE_ID); //offer second instance if needed
    itsApp->offer_service(OTHER_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);    //offer second service if needed
    itsApp->offer_service(SAMPLE_SERVICE_ID, OTHER_SAMPLE_INSTANCE_ID);    //offer second service if needed
    itsApp->offer_service(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID);    //offer second service if needed
}

void VsomeipServer::stop_offer() 
{
    itsApp->stop_offer_service(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    // itsApp->stop_offer_service(SAMPLE_SERVICE_ID, OTHER_SAMPLE_INSTANCE_ID);
    itsApp->stop_offer_service(OTHER_SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID);
    itsApp->stop_offer_service(SAMPLE_SERVICE_ID, OTHER_SAMPLE_INSTANCE_ID);
    itsApp->stop_offer_service(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID);
}

void VsomeipServer::onState(vsomeip::state_type_e _state) 
{
    TRACE(info) << "Application " << itsApp->get_name() << " is " << (_state == vsomeip::state_type_e::ST_REGISTERED ? "registered." : "deregistered.");

    if (_state == vsomeip::state_type_e::ST_REGISTERED) 
    {
    } 
    else 
    {
    }
}

void VsomeipServer::onGet(const std::shared_ptr<vsomeip::message> &_message) 
{
    printMessage(_message);
    std::shared_ptr<vsomeip::message> its_response = vsomeip::runtime::get()->create_response(_message);
    // its_response->set_payload(payload);
    // itsApp->send(its_response);
}

void VsomeipServer::onSet(const std::shared_ptr<vsomeip::message> &_message) 
{
    printMessage(_message);
    std::shared_ptr<vsomeip::message> its_response = vsomeip::runtime::get()->create_response(_message);
    auto payload = _message->get_payload();
    its_response->set_payload(payload);
    itsApp->send(its_response);
    // itsApp->notify(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, payload);
}

void VsomeipServer::onMessage(const std::shared_ptr<vsomeip::message>& message)
{
    std::shared_ptr<vsomeip::payload> its_payload = message->get_payload();
    printMessage(message);

    auto returnCode = static_cast<uint8_t>(message->get_return_code());
    if(returnCode >= 0x01 && returnCode <= 0x1f)
    {
        TRACE(info) << "Ignore Message with return code form 0x01 to 0x1f";
        return;
    }
                                    //|                             for e2e                                 |
    std::vector<vsomeip::byte_t> data {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa};

    auto payload = vsomeip::runtime::get()->create_payload();

    auto msgType = message->get_message_type();
    switch (msgType)
    {
    case vsomeip::message_type_e::MT_REQUEST_NO_RETURN:
        TRACE(info) << "Ignore F&F Message";
        break;
    
    default:
        // Create response
        std::shared_ptr<vsomeip::message> its_response = vsomeip::runtime::get()->create_response(message);
        its_response->set_payload(its_payload);
        itsApp->send(its_response);
        break;
    }

}

void VsomeipServer::onUnknownMethod(const std::shared_ptr<vsomeip::message>& message)
{
    printMessage(message);
    
    auto returnCode = static_cast<uint8_t>(message->get_return_code());
    if(returnCode >= 0x01 && returnCode <= 0x1f)
    {
        TRACE(info) << "Ignore Message with return code form 0x01 to 0x1f";
        return;
    }

    auto msgType = message->get_message_type();
    switch (msgType)
    {
    case vsomeip::message_type_e::MT_REQUEST_NO_RETURN:
        TRACE(info) << "Ignore F&F Message";
        break;
    
    default:
            // Create response
            std::shared_ptr<vsomeip::message> response = vsomeip::runtime::get()->create_message(use_tcp_);
            response->set_service(message->get_service());
            response->set_instance(message->get_instance());
            response->set_method(message->get_method());
            response->set_return_code(vsomeip::return_code_e::E_UNKNOWN_METHOD);
            response->set_interface_version(message->get_interface_version());
            response->set_client(message->get_client());
            response->set_session(message->get_session());
            itsApp->send(response);
        break;
    }
}

bool VsomeipServer::onRegisterSubscription(vsomeip::client_t clientId, vsomeip::uid_t uid, vsomeip::gid_t gid, bool subscribed)
{
    if(subscribed)
    {
        std::cout << "Client/Session ["
                  << std::setw(4) << std::setfill('0') << std::hex << clientId << "/"
                  << "] subscribed. " << std::endl;
        auto payload = vsomeip::runtime::get()->create_payload();
        payload->set_data({'a', 'a', 'a', 'a'});
        // itsApp->notify(SAMPLE_SERVICE_ID, SAMPLE_INSTANCE_ID, SAMPLE_EVENT_ID, payload);

                                                //|                             for e2e                                 |
        std::vector<vsomeip::byte_t> data {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff};
        payload->set_data(data);
        itsApp->notify(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID, E2E_EVENT_ID, payload);

                                            //|                             for e2e                                 |
        std::vector<vsomeip::byte_t> data1 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff};
        payload->set_data(data1);
        itsApp->notify(SAMPLE_SERVICE_ID, E2E_SAMPLE_INSTANCE_ID, E2E_EVENT_ID_2, payload);
    }
}

void VsomeipServer::printMessage(const std::shared_ptr<vsomeip::message> &_message)
{
    std::shared_ptr<vsomeip::payload> payload = _message->get_payload();
    vsomeip::length_t l = payload->get_length();

    // Get payload
    std::stringstream ss;
    for (vsomeip::length_t i=0; i<l; i++) {
       ss << std::setw(2) << std::setfill('0') << std::hex
          << (int)*(payload->get_data()+i) << " ";
    }

    TRACE(debug) << "Received Message with Client/Session ["
        << std::setw(4) << std::setfill('0') << std::hex << _message->get_client() << "/"
        << std::setw(4) << std::setfill('0') << std::hex << _message->get_session() << "] "
        "Service/Instnce ["
        << std::setw(4) << std::setfill('0') << std::hex << _message->get_service() << "/"
        << std::setw(4) << std::setfill('0') << std::hex << _message->get_instance() << "] "
        << ss.str() << std::endl;
}


} // namespace network
